<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Daemon;

use Srv\Libs\Frame\Logs;
use Srv\Libs\Tools\ShellPrint;

class PhpDaemonRun
{
    public static string $homeDir               = '/tmp/';      // 主目录
    public static int $sleep                    = 1;            // 休眠时长[秒]
    public static array $initHandle             = [];           // 初始化回调列表
    public static array $exeHandle              = [];           // 执行回调列表
    public static int $pid                      = 0;            // PID
    public static string $pidUrl                = '';           // PID路径
    public static string $logUrl                = '';           // LOG路径
    public static string $confUrl               = '';           // 配置路径
    public static ?PhpDaemon $PhpDaemon         = null;         // php后台对象

    /**
     * @return void
     * reloadConf
     */
    public static function reloadConf():void
    {
    }

    /**
     * @param PhpDaemon $PhpDaemon
     * @return void
     * start
     */
    public static function start(PhpDaemon $PhpDaemon):void
    {
        self::$PhpDaemon = $PhpDaemon;
        self::init();
        self::reloadConf();
        self::run();
    }

    /**
     * @return void
     * run
     */
    private static function run()
    {
        declare(ticks = 1);
        pcntl_signal(SIGCHLD, SIG_IGN);                     // 忽略信号处理程序(当子进程停止或退出时通知父进程)
        pcntl_signal(SIGHUP, [__CLASS__, 'reloadConf']);    // HUP信号,用来重载配置
        $ppid                   = pcntl_fork();
        if($ppid === -1){                                    // Error
            self::error('ERR: pcntl_fork Error.');
        }else if($ppid === 0){                               // ChildProcess
            if(function_exists('getmypid')){
                self::$pid      = getmypid();
            }else if(function_exists('posix_getpid')){
                self::$pid      = posix_getpid();
            }
            @file_put_contents(self::$pidUrl, self::$pid);
            if(strlen(self::$homeDir) > 0) chdir(self::$homeDir);
            if(!is_array(self::$initHandle) || count(self::$initHandle) < 1) self::error('ERR: initHandle Err');
            if(!is_array(self::$exeHandle) || count(self::$exeHandle) < 1) self::error('ERR: exeHandle Err');
            register_shutdown_function(function(){ Logs::flushAll(); });
            call_user_func_array(self::$initHandle[0], self::$initHandle[1]??[]);
            while(true){
                call_user_func_array(self::$exeHandle[0], self::$exeHandle[1]??[]);
                (self::$sleep > 0) && sleep(self::$sleep);
            }
        }else if($ppid > 0){
            exit(0);
        }
    }

    /**
     * @param string $string
     * @return bool
     * logs
     */
    public static function logs(string $string):bool
    {
        return self::$PhpDaemon->logs($string);
    }

    /**
     * @param string $string
     * @return void
     * error
     */
    private static function error(string $string):void
    {
        ShellPrint::printlnString($string, ShellPrint::COLOR_RED);
        exit(1);
    }

    /**
     * @return void
     * init
     */
    private static function init():void
    {
        self::$logUrl === '' && self::error('ERR: Log Path Empty');
        self::$pidUrl === '' && self::error('ERR: Pid Path Empty');
        !self::iniPhp() && self::error('ERR: Conf PHP FAIL');
        !self::checkRunMode() && self::error('ERR: Please CLI Mode Run');
    }

    /**
     * @return bool
     * iniPhp
     */
    private static function iniPhp():bool
    {
        set_time_limit(0);
        return true;
    }

    /**
     * @return bool
     * checkRunMode
     */
    private static function checkRunMode():bool
    {
        if(function_exists('php_sapi_name')){
            return (php_sapi_name() === 'cli');
        }else if(defined('PHP_SAPI')){
            return (PHP_SAPI === 'cli');
        }
        return false;
    }
}